package eval

/*
import (
	"showen/ast"
	"showen/lex"
	"showen/num"
	"showen/obj"
	"showen/parse"
	"showen/tok"
	"testing"
)

func TestEvalBooleanExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected bool
	}{
		{"#t", true},
		{"#f", false},
		{"(not #t)", false},
		{"(not #f)", true},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		evaluated := testEval(tt.input, env, t)
		testBooleanObject(t, evaluated, tt.expected)
	}
}

func TestIfExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected interface{}
	}{
		{"(if #t 1 2)", 1},
		{"(if #f 1 2)", 2},
		{"(if #t 1)", 1},
		{"(if #f 1)", nil}, // Should return null
		{"(if (> 2 1) 10 20)", 10},
		{"(if (< 2 1) 10 20)", 20},
		{"(if (= 1 1) (+ 2 3) (- 4 1))", 5},
		{"(if (= 1 2) (+ 2 3) (- 4 1))", 3},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		evaluated := testEval(tt.input, env, t)

		if tt.expected == nil {
			// Expecting null object
			if evaluated.Type() != obj.NULL_OBJ {
				t.Errorf("Expected NULL_OBJ, got %v", evaluated.Type())
			}
		} else if expectedInt, ok := tt.expected.(int); ok {
			testNumberObject(t, evaluated, expectedInt)
		}
	}
}

func TestQuoteExpression(t *testing.T) {
	tests := []struct {
		input    string
		expected string
	}{
		{"'1", "1"},
		{"'#t", "#t"},
		{"'hello", "hello"},
		{"'(1 2 3)", "(1 2 3)"},
		{"'((1 2) (3 4))", "((1 2) (3 4))"},
		// {"'(1 . 2)", "(1 . 2)"},  // Skip dotted pair test for now
		{"\"hello\"", "\"hello\""},
		{"'\"hello\"", "\"hello\""},
	}

	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	for _, tt := range tests {
		evaluated := testEval(tt.input, env, t)
		if evaluated.Inspect() != tt.expected {
			t.Errorf("quote test failed: input=%s, expected=%s, got=%s",
				tt.input, tt.expected, evaluated.Inspect())
		}
	}
}

func testEval(input string, env *obj.Environment, t *testing.T) obj.Object {
	l := lex.NewLexer(input)
	tokens := l.ReadTokens()
	p := parse.NewParser(tokens)
	expr, err := p.ParseExpression()
	if err != nil {
		t.Fatalf("ParseExpression failed: %s", err)
	}

	evaluated, err := EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("EvalExpression failed: %s", err)
	}

	return evaluated
}

func testBooleanObject(t *testing.T, o obj.Object, expected bool) bool {
	result, ok := o.(*obj.BooleanObject)
	if !ok {
		t.Errorf("object is not BooleanObject. got=%T (%+v)", o, o)
		return false
	}
	if result.Value != expected {
		t.Errorf("object has wrong value. got=%t, want=%t", result.Value, expected)
		return false
	}
	return true
}

func testNumberObject(t *testing.T, o obj.Object, expected int) bool {
	result, ok := o.(*obj.NumberObject)
	if !ok {
		t.Errorf("object is not NumberObject. got=%T (%+v)", o, o)
		return false
	}

	numValue, ok := result.Value.(num.Integer)
	if !ok {
		t.Errorf("NumberObject value is not Integer. got=%T", result.Value)
		return false
	}

	if int64(numValue) != int64(expected) {
		t.Errorf("object has wrong value. got=%d, want=%d", int64(numValue), int64(expected))
		return false
	}
	return true
}

func TestEvalIntegerExpression(t *testing.T) {
	tests := []struct {
		name     string
		expr     ast.Exp
		expected int64
	}{
		{
			name:     "positive integer",
			expr:     &ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "123", Literal: num.Integer(int64(123))}},
			expected: 123,
		},
		{
			name:     "negative integer",
			expr:     &ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "-456", Literal: num.Integer(int64(-456))}},
			expected: -456,
		},
		{
			name:     "zero integer",
			expr:     &ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "0", Literal: num.Integer(int64(0))}},
			expected: 0,
		},
		{
			name:     "max int64",
			expr:     &ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "9223372036854775807", Literal: num.Integer(int64(9223372036854775807))}},
			expected: 9223372036854775807,
		},
		{
			name:     "min int64",
			expr:     &ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "-9223372036854775808", Literal: num.Integer(int64(-9223372036854775808))}},
			expected: -9223372036854775808,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			env := obj.NewEnvironment()
			result, err := EvalExpression(tt.expr, env)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			intObj, ok := result.(*obj.NumberObject)
			if !ok {
				t.Fatalf("expected IntegerObject, got %T", result)
			}

			if int64(intObj.Value.(num.Integer)) != tt.expected {
				t.Errorf("IntegerObject.Value = %v, want %v", intObj.Value, tt.expected)
			}
		})
	}
}

func TestEvalIdentifierExpression(t *testing.T) {
	// Test successful identifier lookup
	env := obj.NewEnvironment()
	env.Set("x", &obj.BooleanObject{Value: true})

	expr := &ast.IdentifierExp{
		Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x", Literal: "x", Line: 1, Column: 1},
	}

	result, err := EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	booleanObj, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Fatalf("expected BooleanObject, got %T", result)
	}

	if !booleanObj.Value {
		t.Errorf("BooleanObject.Value = %v, want true", booleanObj.Value)
	}

	// Test undefined identifier error
	undefinedExpr := &ast.IdentifierExp{
		Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "y", Literal: "y", Line: 1, Column: 1},
	}

	_, err = EvalExpression(undefinedExpr, env)
	if err == nil {
		t.Fatalf("expected error for undefined identifier, got none")
	}

	if err.Error() != "identifier not found: y" {
		t.Errorf("unexpected error message: %v", err.Error())
	}
}

func TestEvalEmptyListExpression(t *testing.T) {
	expr := &ast.ListExp{
		Exps: []ast.Exp{},
	}

	env := obj.NewEnvironment()
	result, err := EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	_, ok := result.(*obj.NullObject)
	if !ok {
		t.Fatalf("expected NullObject, got %T", result)
	}
}

func TestEvalDefineExpression(t *testing.T) {
	// Test define with value
	defineExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "define"}},
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x"}},
			&ast.BooleanExp{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t", Literal: true}},
		},
	}

	env := obj.NewEnvironment()
	result, err := EvalExpression(defineExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// Check that the result is a null object
	_, ok := result.(obj.NullObject)
	if !ok {
		t.Fatalf("expected NullObject, got %T", result)
	}

	// Check that the variable was defined in the environment
	val, ok := env.Get("x")
	if !ok {
		t.Fatalf("expected variable 'x' to be defined")
	}

	booleanObj, ok := val.(*obj.BooleanObject)
	if !ok {
		t.Fatalf("expected BooleanObject, got %T", val)
	}

	if !booleanObj.Value {
		t.Errorf("BooleanObject.Value = %v, want true", booleanObj.Value)
	}

	// Test define with insufficient arguments
	invalidDefineExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "define"}},
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x"}},
		},
	}

	_, err = EvalExpression(invalidDefineExpr, env)
	if err == nil {
		t.Fatalf("expected error for insufficient arguments, got none")
	}

	if err.Error() != "define requires at least 2 arguments" {
		t.Errorf("unexpected error message: %v", err.Error())
	}
}

func TestEvalDefineProcedureExpression(t *testing.T) {
	// Test define with procedure
	procDefExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "define"}},
			&ast.ListExp{
				Exps: []ast.Exp{
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "add"}},
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x"}},
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "y"}},
				},
			},
			&ast.ListExp{
				Exps: []ast.Exp{
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "+"}},
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x"}},
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "y"}},
				},
			},
		},
	}

	env := obj.NewEnvironment()
	result, err := EvalExpression(procDefExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	// Check that the result is a procedure
	_, ok := result.(*obj.Procedure)
	if !ok {
		t.Fatalf("expected Procedure, got %T", result)
	}

	// Check that the procedure was defined in the environment
	val, ok := env.Get("add")
	if !ok {
		t.Fatalf("expected procedure 'add' to be defined")
	}

	_, ok = val.(*obj.Procedure)
	if !ok {
		t.Fatalf("expected Procedure, got %T", val)
	}
}

func TestEvalLetExpression(t *testing.T) {
	// Test let expression
	letExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "let", Literal: "let"}},
			&ast.ListExp{
				Exps: []ast.Exp{
					&ast.ListExp{
						Exps: []ast.Exp{
							&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x", Literal: "x"}},
							&ast.BooleanExp{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t", Literal: true}},
						},
					},
				},
			},
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x", Literal: "x"}},
		},
	}

	env := obj.NewEnvironment()
	result, err := EvalExpression(letExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	booleanObj, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Fatalf("expected BooleanObject, got %T", result)
	}

	if !booleanObj.Value {
		t.Errorf("BooleanObject.Value = %v, want true", booleanObj.Value)
	}

	// Test let with insufficient arguments
	invalidLetExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "let"}},
		},
	}

	_, err = EvalExpression(invalidLetExpr, env)
	if err == nil {
		t.Fatalf("expected error for insufficient arguments, got none")
	}

	if err.Error() != "let requires at least bindings and one body expression" {
		t.Errorf("unexpected error message: %v", err.Error())
	}
}

func TestEvalProcedureCall(t *testing.T) {
	// First define a simple procedure
	env := obj.NewEnvironment()

	// Create a builtin function for testing
	builtin := &obj.Builtin{
		Fn: func(args []obj.Object, env *obj.Environment) obj.Object {
			return &obj.BooleanObject{Value: true}
		},
	}
	env.Set("test-func", builtin)

	// Call the procedure
	procCallExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "test-func"}},
		},
	}

	result, err := EvalExpression(procCallExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	booleanObj, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Fatalf("expected BooleanObject, got %T", result)
	}

	if !booleanObj.Value {
		t.Errorf("BooleanObject.Value = %v, want true", booleanObj.Value)
	}

	// Test calling undefined procedure
	undefinedProcCallExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "undefined-func"}},
		},
	}

	_, err = EvalExpression(undefinedProcCallExpr, env)
	if err == nil {
		t.Fatalf("expected error for undefined procedure, got none")
	}

	if err.Error() != "undefined: undefined-func" {
		t.Errorf("unexpected error message: %v", err.Error())
	}
}

func TestEvalProgram(t *testing.T) {
	program := []ast.Exp{
		&ast.ListExp{
			Exps: []ast.Exp{
				&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "define"}},
				&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x", Literal: "x"}},
				&ast.BooleanExp{Token: tok.Token{Type: tok.BOOLEAN, Lexeme: "#t", Literal: true}},
			},
		},
		&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "x", Literal: "x"}},
	}

	env := obj.NewEnvironment()
	result, err := EvalProgram(program, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	booleanObj, ok := result.(*obj.BooleanObject)
	if !ok {
		t.Fatalf("expected BooleanObject, got %T", result)
	}

	if !booleanObj.Value {
		t.Errorf("BooleanObject.Value = %v, want true", booleanObj.Value)
	}
}

func TestEvalArithmeticOperations(t *testing.T) {
	// Create environment with builtin procedures
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test addition: (+ 1 2 3)
	addExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "+"}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "1", Literal: num.Integer(int64(1))}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "2", Literal: num.Integer(int64(2))}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "3", Literal: num.Integer(int64(3))}},
		},
	}

	result, err := EvalExpression(addExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	intObj, ok := result.(*obj.NumberObject)
	if !ok {
		t.Fatalf("expected IntegerObject, got %T", result)
	}

	if int64(intObj.Value.(num.Integer)) != int64(6) {
		t.Errorf("Expected (+ 1 2 3) to be 6, got %v", int64(intObj.Value.(num.Integer)))
	}

	// Test subtraction: (- 10 3 2)
	subExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "-"}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "10", Literal: num.Integer(int64(10))}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "3", Literal: num.Integer(int64(3))}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "2", Literal: num.Integer(int64(2))}},
		},
	}

	result, err = EvalExpression(subExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	intObj, ok = result.(*obj.NumberObject)
	if !ok {
		t.Fatalf("expected IntegerObject, got %T", result)
	}

	if int64(intObj.Value.(num.Integer)) != int64(5) {
		t.Errorf("Expected (- 10 3 2) to be 5, got %v", int64(intObj.Value.(num.Integer)))
	}

	// Test multiplication: (* 2 3 4)
	mulExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "*"}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "2", Literal: num.Integer(int64(2))}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "3", Literal: num.Integer(int64(3))}},
			&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "4", Literal: num.Integer(int64(4))}},
		},
	}

	result, err = EvalExpression(mulExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	intObj, ok = result.(*obj.NumberObject)
	if !ok {
		t.Fatalf("expected IntegerObject, got %T", result)
	}

	if int64(intObj.Value.(num.Integer)) != int64(24) {
		t.Errorf("Expected (* 2 3 4) to be 24, got %v", int64(intObj.Value.(num.Integer)))
	}

	// Test complex expression: (+ (* 2 3) (- 10 5))
	// This is equivalent to (+ 6 5) = 11
	complexExpr := &ast.ListExp{
		Exps: []ast.Exp{
			&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "+"}},
			&ast.ListExp{
				Exps: []ast.Exp{
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "*"}},
					&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "2", Literal: num.Integer(int64(2))}},
					&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "3", Literal: num.Integer(int64(3))}},
				},
			},
			&ast.ListExp{
				Exps: []ast.Exp{
					&ast.IdentifierExp{Token: tok.Token{Type: tok.IDENTIFIER, Lexeme: "-"}},
					&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "10", Literal: num.Integer(int64(10))}},
					&ast.NumberExp{Token: tok.Token{Type: tok.INTEGER, Lexeme: "5", Literal: num.Integer(int64(5))}},
				},
			},
		},
	}

	result, err = EvalExpression(complexExpr, env)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	intObj, ok = result.(*obj.NumberObject)
	if !ok {
		t.Fatalf("expected IntegerObject, got %T", result)
	}

	if intObj.Value.(num.Integer).Value != 11 {
		t.Errorf("Expected (+ (* 2 3) (- 10 5)) to be 11, got %v", intObj.Value)
	}
}
*/
